
example_class_definition = [[
class_definition = 
{
    -- optional constructor 
    construct = function(self, args)
        self.num = 3
        self.text = "hello"
    end,

    -- method definitions
    f = function(self, x) 
        return x * self.num 
    end,

    -- properties with default values
    num2 = 5,
    text2 = "hello2",
}
]]

local classes = {}

local class = function(name, class_definition)
    local mt = {construct = function() end}

    if type(name) ~= "string" then
        error("name must be string")
    end

    if type(class_definition) ~= "table" then
        error("type definition must be table")
    end

    if classes[name] then
        error("class " .. name .. " already defined")
    end

    for i, v in pairs(class_definition) do
        mt[i] = v
    end

    mt.__index = function(self, key)
        member = mt[key]
        return member
    end

    mt.__newindex = function(self, key, value)
        mt[key] = value
    end

    classes[name] = mt
    return mt
end

local remove = function(name)
    classes[name] = nil
end

local new = function(class_object_or_class_name, ...)
    t = {}
    local class_object = nil
    if(type(class_object_or_class_name) == "string") then
        class_object = classes[class_object_or_class_name]
        if not class_object then
            error("Type ".. class_object_or_class_name .. "not defined")
        end
    else
        class_object = class_object_or_class_name
    end

    setmetatable(t, class_object)
    -- note: arg is {...}
    t:construct(unpack(arg))
    return t
end

oo = 
{
    class = class,
    new = new,
    classes = classes,
    remove = remove,
}

--for k, v in pairs(oo) do _G[k] = v end

